#include <types.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

int do_printf(const char *fmt, va_list args, int fn(uint_32, void **), void *ptr)
{
  int count = 0;
  int i,xbase;
  unsigned int num;
  uint_8 buff[20];
  
  for(; *fmt; fmt++)
  {
    switch(*fmt)
    {
    case '\\':
      {
	fmt++;
	switch(*fmt)
	{
	case '\\':
	  fn(*fmt, &ptr);
	  break;
	case 'n':
	  fn('\n', &ptr);
	  break;
	case 'b':
	  fn('\b', &ptr);
	  break;
	case 't':
	  fn('\t', &ptr);
	  break;

	}
	count++;
	continue;
      }

    case '%':
      {
	fmt++;
	switch(*fmt)
	{
	case 'c':
	  num = va_arg(args, char);
	  fn(num, &ptr);
	  break;
	  
	case 'd':
	  i = 0;
	  num = va_arg(args, int);
	  if (num == 0) {
	    fn('0', &ptr);
	    break;
	  }

	  if((signed int)num < 0)
	  {
	    num = 0 - (signed int) num;
	    fn('-', &ptr);
	  }
	  
	  while(num)
	  {
	    buff[++i] = num % 10;
	    num = num / 10;
	  }

	  while(i)
	    fn(buff[i--] + '0', &ptr);
	  break;
	  	  
	case 'u':
	  i = 0;
	  num = va_arg(args, unsigned int);
	  if (num == 0) {
	    fn('0', &ptr);
	    break;
	  }
  
	  while(num)
	  {
	    buff[++i] = num % 10;
	    num = num / 10;
	  }

	  while(i)
	    fn(buff[i--] + '0', &ptr);
	  break;

	case 'X':
	  xbase = 'A';
	  goto __putX;
	  
	case 'x':
	  xbase = 'a';
	__putX:	  num = va_arg(args, unsigned int);
	  if (num == 0) {
	    fn('0', &ptr);
	    break;
	  }
	  i = 0;
	  while(num)
	  {
	    if ((num&0xF)<0x0a)
	      buff[++i]='0'+(num&0xF);
	    else buff[++i]=xbase+(num&0xF)-10;
	    num>>=4;
	  }

	  while(i)
	    fn(buff[i--], &ptr);
	  break;
      
	case 'o':
	  num = va_arg(args, unsigned int);
	  if (num == 0) {
	    fn('0', &ptr);
	    break;
	  }
	  i = 0;
	  while(num)
	  {
	    buff[++i]='0'+(num&7);
	    num>>=3;
	  }
	  while(i)
	    fn(buff[i--], &ptr);
	  break;

	case 's':
	  num = va_arg(args, unsigned int);
	  while(*(char *)num)
	  {
	    fn(*(char *)num++, &ptr);
	    count++;
	  }
	  continue;
	}
	count++;
	continue;
      }
      
    default:
      {
	fn(*fmt, &ptr);	/* ...just echo it */
	count++;
	break;
      }
    }
  }
  return count;
}


      /*****************************************************************************
SPRINTF
      *****************************************************************************/
int vsprintf_help(unsigned c, void **ptr)
{
  char *dst;

  dst = *ptr;
  *dst++ = c;
  *ptr = dst;
  return 0 ;
}
      /*****************************************************************************
       *****************************************************************************/
int vsprintf(char *buffer, const char *fmt, va_list args)
{
  int ret_val;

  ret_val = do_printf(fmt, args, vsprintf_help, (void *)buffer);
  buffer[ret_val] = '\0';
  return ret_val;
}
      /*****************************************************************************
       *****************************************************************************/
int sprintf(char *buffer, const char *fmt, ...)
{
  va_list args;
  int ret_val;

  va_start(args, fmt);
  ret_val = vsprintf(buffer, fmt, args);
  va_end(args);
  return ret_val;
}
      /*****************************************************************************
PRINTF
You must write your own putchar()
      *****************************************************************************/
int vprintf_help(unsigned c, void **ptr)
{
  putchar(c);
  return 0 ;
}
      /*****************************************************************************
       *****************************************************************************/
int vprintf(const char *fmt, va_list args)
{
  return do_printf(fmt, args, vprintf_help, NULL);
}
      /*****************************************************************************
       *****************************************************************************/
int printf(const char *fmt, ...)
{
  va_list args;
  int ret_val;
  
  va_start(args, fmt);

  ret_val = vprintf(fmt, args);
  va_end(args);
  return ret_val;
}
